/*
 * @Copyright: Copyright (c) 2030 吴周海
 * @Author: wzh200x@126.com
 * @Date: 2025-10-16 15:38:56
 * @LastEditors: wzh200x@126.com
 * @LastEditTime: 2025-10-22 15:44:22
 * @FilePath: \gogamewebserver\goGameWebServer\internal\platform\platformBytejump.go
 * @Description: 文件功能描述
 */
package platform

import (
	"bytes"
	"encoding/json"
	"fmt"
	"goGameWebServer/internal/common/config"
	"goGameWebServer/internal/common/logger"
	"goGameWebServer/internal/common/utils"
	"goGameWebServer/internal/manager/cachemanager"
	"io"
	"net/http"
	"time"

	"github.com/gogf/gf/v2/util/gconv"
)

// 微信登录返回的会话信息
type ByteJumpSession struct {
	OpenID     string `json:"openid"`
	SessionKey string `json:"session_key"`
	UnionID    string `json:"unionid,omitempty"`
	ErrCode    int    `json:"errcode,omitempty"`
	ErrMsg     string `json:"errmsg,omitempty"`
}

// AccessToken响应结构体
type AccessTokenResponse struct {
	ErrCode int    `json:"err_no"`
	ErrMsg  string `json:"err_tips"`
	Data    struct {
		AccessToken string `json:"access_token"`
		ExpiresAt   int64  `json:"expires_at"`
	} `json:"data"`
}

// AccessToken结果结构体
type AccessTokenResult struct {
	AccessToken string `json:"accessToken"`
	ExpireTime  int64  `json:"expireTime"`
}

// ECPM响应结构体
type EcpmResponse struct {
	ErrCode int    `json:"err_no"`
	ErrMsg  string `json:"err_tips"`
	Data    struct {
		Records []EcpmRecord `json:"records"`
	} `json:"data"`
}

// ECPM记录结构体
type EcpmRecord struct {
	Cost float64 `json:"cost"`
}

type PlatformByteJump struct {
	PlatformBase
}

func (p *PlatformByteJump) Login(code string, option *config.PlatformOption) (output map[string]interface{}, err error) {
	// 1. 通过code获取微信会话信息
	session, err := p.GetSession(option.AppId, option.AppSecret, code)
	if err != nil {
		return output, err
	}
	output = make(map[string]interface{})
	output["open_id"] = session.OpenID
	output["session_key"] = session.SessionKey
	return output, nil
}

// 通过code获取微信会话信息
func (p *PlatformByteJump) GetSession(appid string, appSecret string, code string) (*ByteJumpSession, error) {
	url := fmt.Sprintf("https://minigame.zijieapi.com/mgplatform/api/apps/jscode2session?appid=%s&secret=%s&code=%s",
		appid, appSecret, code)

	resp, err := http.Get(url)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	var session ByteJumpSession
	if err := json.Unmarshal(body, &session); err != nil {
		return nil, err
	}

	if session.ErrCode != 0 {
		return nil, fmt.Errorf("抖音API错误: %d %s", session.ErrCode, session.ErrMsg)
	}

	return &session, nil
}

func (p *PlatformByteJump) Report(params map[string]interface{}, option *config.PlatformOption) (re map[string]interface{}, err error) {
	re = make(map[string]interface{})
	// 构建请求参数
	reqParams := map[string]interface{}{
		"event_type": params["action_code"],
		"context": map[string]interface{}{
			"ad": map[string]interface{}{
				"callback": params["action_finish_channel"],
			},
		},
		"timestamp": utils.GetNowTimestampSecond(),
	}
	// 发送POST请求
	// 将参数转换为JSON
	jsonData, err := json.Marshal(reqParams)
	if err != nil {
		logger.LoggerIns().Errorf("参数序列化失败: %v\n", err)
		return re, err
	}
	resp, err := http.Post("https://analytics.oceanengine.com/api/v2/conversion",
		"application/json", bytes.NewBuffer(jsonData))
	if err != nil {
		logger.LoggerIns().Errorf("抖音行动码上报请求失败: %v\n", err)
		return re, err
	}
	defer resp.Body.Close()

	// 读取响应体
	body, err := io.ReadAll(resp.Body)
	if err != nil {
		logger.LoggerIns().Errorf("读取抖音行动码上报响应失败: %v\n", err)
		return re, err
	}
	// 解析响应体
	var response map[string]interface{}
	err = json.Unmarshal(body, &response)
	if err != nil {
		logger.LoggerIns().Errorf("响应体解析失败: %v\n", err)
		return re, err
	}
	// 检查响应状态
	if gconv.String(response["code"]) != "0" {
		logger.LoggerIns().Errorf("抖音行动码上报失败: %v\n", response)
		logger.LoggerIns().Errorf("抖音行动码上报内容: %v\n", params)
	}
	re["code"] = response["code"]
	re["msg"] = response["msg"]
	re["result"] = body
	return re, nil
}

func (p *PlatformByteJump) Ecpm(params map[string]interface{}, option *config.PlatformOption) (re map[string]interface{}, err error) {
	re = make(map[string]interface{})
	re["cost"] = 0
	re["total"] = 0

	// 判断是否存在openId字段
	if _, ok := params["openId"]; !ok {
		logger.LoggerIns().Warnf("缺少openId字段")
		return re, nil // 返回默认值而不是错误
	}

	openid := gconv.String(params["openId"])
	if openid == "" {
		logger.LoggerIns().Warnf("openId为空")
		return re, nil // 返回默认值
	}

	// 获取有效的AccessToken
	accessToken, err := p.GetValidAccessToken(option)
	if err != nil {
		logger.LoggerIns().Errorf("获取AccessToken失败: %v", err)
		return re, nil // 返回默认值而不是错误
	}

	ecpmCost := 0.0
	ecpmTotal := 0

	// 查询三天的ECPM
	for i := 0; i < 3; i++ {
		// 获取指定天数前的日期
		dateStr := utils.GetSubDate(i)

		// 构建请求URL
		url := fmt.Sprintf("https://minigame.zijieapi.com/mgplatform/api/apps/data/get_ecpm?open_id=%s&mp_id=%s&date_hour=%s&access_token=%s&page_no=1&page_size=30",
			openid, option.AppId, dateStr, accessToken)

		// 发送GET请求
		resp, err := http.Get(url)
		if err != nil {
			logger.LoggerIns().Errorf("查询ECPM请求失败，日期: %s, Error: %v", dateStr, err)
			continue // 继续查询下一天
		}

		// 读取响应体
		body, err := io.ReadAll(resp.Body)
		resp.Body.Close()
		if err != nil {
			logger.LoggerIns().Errorf("读取ECPM响应失败，日期: %s, Error: %v", dateStr, err)
			continue
		}

		// 解析响应体
		var response EcpmResponse
		err = json.Unmarshal(body, &response)
		if err != nil {
			logger.LoggerIns().Errorf("ECPM响应体解析失败，日期: %s, Error: %v", dateStr, err)
			continue
		}

		// 检查响应状态
		if response.ErrCode != 0 {
			logger.LoggerIns().Errorf("查询ECPM失败，日期: %s, ErrCode: %d, ErrMsg: %s", dateStr, response.ErrCode, response.ErrMsg)
			continue
		}

		// 处理记录数据
		records := response.Data.Records
		for _, record := range records {
			ecpmCost += record.Cost
			ecpmTotal++
		}

		logger.LoggerIns().Infof("查询ECPM成功，日期: %s, 记录数: %d", dateStr, len(records))
	}

	re["cost"] = ecpmCost
	re["total"] = ecpmTotal

	// logger.LoggerIns().Infof("ECPM查询完成，OpenId: %s, 总成本: %.2f, 总记录数: %d", openid, ecpmCost, ecpmTotal)
	return re, nil
}

// UpdateAccessToken 更新访问令牌
func (p *PlatformByteJump) UpdateAccessToken(option *config.PlatformOption) (*AccessTokenResult, error) {
	// 构建请求参数
	reqParams := map[string]interface{}{
		"grant_type": "client_credential",
		"appid":      option.AppId,
		"secret":     option.AppSecret,
	}

	// 将参数转换为JSON
	jsonData, err := json.Marshal(reqParams)
	if err != nil {
		logger.LoggerIns().Errorf("参数序列化失败: %v", err)
		return nil, err
	}

	// 发送POST请求
	resp, err := http.Post("https://minigame.zijieapi.com/mgplatform/api/apps/v2/token",
		"application/json", bytes.NewBuffer(jsonData))
	if err != nil {
		logger.LoggerIns().Errorf("更新AccessToken请求失败: %v", err)
		return nil, err
	}
	defer resp.Body.Close()

	// 读取响应体
	body, err := io.ReadAll(resp.Body)
	if err != nil {
		logger.LoggerIns().Errorf("读取AccessToken响应失败: %v", err)
		return nil, err
	}

	// 解析响应体
	var response AccessTokenResponse
	err = json.Unmarshal(body, &response)
	if err != nil {
		logger.LoggerIns().Errorf("AccessToken响应体解析失败: %v", err)
		return nil, err
	}

	// 检查响应状态
	if response.ErrCode != 0 {
		logger.LoggerIns().Errorf("获取AccessToken失败: %d %s", response.ErrCode, response.ErrMsg)
		return nil, fmt.Errorf("获取AccessToken失败: %d %s", response.ErrCode, response.ErrMsg)
	}

	// 检查data字段是否存在
	if response.Data.AccessToken == "" {
		logger.LoggerIns().Errorf("AccessToken为空")
		return nil, fmt.Errorf("AccessToken为空")
	}

	// 构建返回结果
	result := &AccessTokenResult{
		AccessToken: response.Data.AccessToken,
		ExpireTime:  response.Data.ExpiresAt,
	}

	// 保存AccessToken到缓存
	err = p.setAccessToken(option.AppId, result.AccessToken, result.ExpireTime)
	if err != nil {
		logger.LoggerIns().Errorf("保存AccessToken到缓存失败: %v", err)
		return nil, err
	}

	logger.LoggerIns().Infof("AccessToken更新成功，AppId: %s, 过期时间: %d", option.AppId, result.ExpireTime)
	return result, nil
}

// setAccessToken 保存AccessToken到缓存
func (p *PlatformByteJump) setAccessToken(appId, accessToken string, expireTime int64) error {
	// 构建缓存key，包含appId以便区分不同应用
	cacheKey := fmt.Sprintf("bytejump:access_token:%s", appId)

	// 计算过期时间（秒）
	// expireTime是Unix时间戳，需要转换为相对于当前时间的秒数
	currentTime := time.Now().Unix()
	expireSeconds := int(expireTime - currentTime)

	// 如果token已经过期或即将过期（小于60秒），则不保存
	if expireSeconds <= 60 {
		logger.LoggerIns().Warnf("AccessToken即将过期或已过期，不保存到缓存，AppId: %s, 剩余时间: %d秒", appId, expireSeconds)
		return fmt.Errorf("AccessToken即将过期或已过期")
	}

	// 保存到缓存
	err := cachemanager.ManagerCache().Put(cacheKey, accessToken, expireSeconds)
	if err != nil {
		logger.LoggerIns().Errorf("保存AccessToken到缓存失败，AppId: %s, Error: %v", appId, err)
		return err
	}

	logger.LoggerIns().Infof("AccessToken已保存到缓存，AppId: %s, Key: %s, 过期时间: %d秒", appId, cacheKey, expireSeconds)
	return nil
}

// getAccessToken 从缓存获取AccessToken
func (p *PlatformByteJump) getAccessToken(appId string) (string, error) {
	// 构建缓存key
	cacheKey := fmt.Sprintf("bytejump:access_token:%s", appId)

	// 从缓存获取
	accessToken, err := cachemanager.ManagerCache().Get(cacheKey)
	if err != nil {
		logger.LoggerIns().Errorf("从缓存获取AccessToken失败，AppId: %s, Error: %v", appId, err)
		return "", err
	}

	if accessToken == "" {
		logger.LoggerIns().Warnf("缓存中未找到AccessToken，AppId: %s", appId)
		return "", fmt.Errorf("缓存中未找到AccessToken")
	}

	logger.LoggerIns().Infof("从缓存获取AccessToken成功，AppId: %s", appId)
	return accessToken, nil
}

// GetValidAccessToken 获取有效的AccessToken（优先从缓存获取，如果缓存中没有则重新获取）
func (p *PlatformByteJump) GetValidAccessToken(option *config.PlatformOption) (string, error) {
	// 先尝试从缓存获取
	accessToken, err := p.getAccessToken(option.AppId)
	if err == nil && accessToken != "" {
		return accessToken, nil
	}

	// 缓存中没有或获取失败，重新获取AccessToken
	// logger.LoggerIns().Infof("缓存中无有效AccessToken，重新获取，AppId: %s", option.AppId)
	result, err := p.UpdateAccessToken(option)
	if err != nil {
		return "", err
	}

	return result.AccessToken, nil
}

func NewPlatformByteJump() *PlatformByteJump {
	return &PlatformByteJump{
		PlatformBase: PlatformBase{},
	}
}
